package com.tm.nettystudy.delimiterBasedFrame;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyServer {
    public static void main(String[] args) throws Exception {

        //创建两个线程组bossGroup和workerGroup,
        //NioEventLoop的个数默认为cpu核数的两倍
        // bossGroup只是处理连接请求 ,真正的和客户端业务处理，workerGroup完成
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup (8);
        try {
            //创建服务器端的引导对象
            ServerBootstrap bootstrap = new ServerBootstrap ();
            //将两个线程组放进引导对象
            bootstrap.group (bossGroup, workerGroup)
                    //NioServerSocketChannel服务端channel
                    .channel (NioServerSocketChannel.class)
                    // 初始化服务器连接队列大小，服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
                    // 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
                    .option (ChannelOption.SO_BACKLOG, 1024)
                    .childHandler (new ChannelInitializer<SocketChannel>() {//创建通道初始化对象，设置初始化参数
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //每一个链接过来(每一次创建channel)都会执行这个方法
                            System.out.println ("初始化pipeline");
                            //编码,将String转码为ByteBuf
                            ch.pipeline ().addLast ("encode", new StringEncoder());
                            //用分割符处理粘包问题,分隔符可以是多个,
                            // 1024代表1024个字节内还没有找到分隔符抛出异常,TooLongFrameException,
                            //如果后续的Handler重写了exceptionCaught方法就会调用exceptionCaught方法
                            //DelimiterBasedFrameDecoder分割是转义成ByteBuf才分割的,所以如果添加了StringDecoder的话,
                            // 要把StringDecoder放在DelimiterBasedFrameDecoder的后面
                            ByteBuf delimiter = Unpooled.copiedBuffer ("$_$".getBytes ());
                            ch.pipeline ().addLast (new DelimiterBasedFrameDecoder(1024, delimiter));
                            //解码,将ByteBuf解码为String
                            ch.pipeline ().addLast ("decode", new StringDecoder());
                            //handler一般都是放在最后面,建议一个就可以,如果业务复杂拆分多个,那在handler中调用fireXXX才会向下一个handler继续调用
                            ch.pipeline ().addLast (new NettyServerHandler());
                        }
                    });
            System.out.println ("netty server start。。");
            //绑定一个端口并且同步, 生成了一个ChannelFuture异步对象，通过isDone()等方法可以判断异步事件的执行情况
            //启动服务器(并绑定端口)，bind是异步操作，sync方法是等待异步操作执行完毕
            //我们可以去掉sync,添加事件监听,如果链接成功失败相对应的处理
            ChannelFuture cf = bootstrap.bind (9000);
            //给cf注册监听器，监听我们关心的事件
            cf.addListener (new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess ()) {
                        //这里只是简单的打印,
                        System.out.println ("监听端口9000成功");
                    } else {
                        System.out.println ("监听端口9000失败");
                    }
                }
            });
            //对通道关闭进行监听，closeFuture是异步操作，监听通道关闭
            // 通过sync方法同步等待通道关闭处理完毕，这里会阻塞等待通道关闭完成
            cf.channel ().closeFuture ().sync ();
        } finally {
            bossGroup.shutdownGracefully ();
            workerGroup.shutdownGracefully ();
        }
    }
}

